﻿using System.Collections.Generic;
using System.Linq;

namespace KxParser.Parser.LR
{
    class DfaEx
    {
        private List<StateEx> _dfaStates = new List<StateEx>();

        public DfaEx()
        {
            foreach (var productionItem in KxParserGlobal.ProductionItemsEx)
            {
                if(productionItem.Name.Equals(KxParserGlobal.GrammarStarter))
                {
					var lastValue = productionItem.RuleItems.LastOrDefault();
                    if (lastValue != null && !lastValue.Equals(KxParserGlobal.Ender))
						productionItem.RuleItems.Add(KxParserGlobal.Ender);

                    // Create initial state
                    var state = new StateEx(_dfaStates.Count + 1);
                    state.ProductionItems.Add(productionItem);
                    state.CreateClosure();

                    _dfaStates.Add(state);

                    break; // Take first START
                }
            }

            createAllStates();
        }

        public StateEx[] GetStates()
        {
            return _dfaStates.ToArray();
        }

        void createAllStates()
        {
            if (_dfaStates[0] == null) return; 

            var statesQueue = new Queue<StateEx>();
            statesQueue.Enqueue(_dfaStates[0]);

            // Create new state for each possible transition
            while (statesQueue.Count > 0)
            {
                var currentState = statesQueue.Dequeue();

                var nextPaths = currentState.GetNextPaths();

                foreach (var nextPath in nextPaths)
                {
                    var selectedProductionItems = (from productionItem in currentState.ProductionItems
                                                   where productionItem.CanConsume(nextPath)
                                                   select productionItem);

                    StateEx newState = new StateEx(_dfaStates.Count + 1);
                    newState.ProductionItems.AddRange(from productionItem in currentState.ProductionItems
                                      where productionItem.CanConsume(nextPath)
                                      select productionItem.Consume(nextPath));
                    newState.CreateClosure();

                    StateEx compatibleState = _dfaStates.FirstOrDefault(state => state.Equals(newState));

                    if (compatibleState == null)
                    {
                        foreach (var item in selectedProductionItems)
                        {
                            item.NextState = newState;
                            item.OverItem = nextPath;
                        }

                        _dfaStates.Add(newState);
                        statesQueue.Enqueue(newState);
                    }
                    else
                    {
                        foreach (var item in selectedProductionItems)
                        {
                            item.NextState = compatibleState;
                            item.OverItem = nextPath;
                        }
                    }
                }
            }
            
            KxParserGlobal.States.AddRange(_dfaStates);
        }
    }
}
